<?php
/**
 *       FILENAME:               /libraries/wildcard.php
 *       DESCRIPTION:    This library that uses the PHP Snoopy library to interact with Villanova's Office of the Wildcard website.
 *       AUTHOR:                 Casey Burkhardt
 *       VERSION:                1.0.0
 *       LAST MODIFIED:  2/23/2010
 **/

require_once($_SERVER['DOCUMENT_ROOT'] . "/config/environment.php");
require_once($_SERVER['DOCUMENT_ROOT'] . "/libraries/Snoopy.class.php");

// Define Constants
global $WILDCARD_AUTH_URL, $WILDCARD_ACCOUNT_URL, $WILDCARD_TRANSACTION_DETAIL_URL, $WILDCARD_LOGIN_CHECK_URL, $SKEY_LENGTH, $HEADER_DATA;
$WILDCARD_AUTH_URL = "https://services.jsatech.com/login.php";
$WILDCARD_ACCOUNT_URL = "https://services.jsatech.com/index.php";
$WILDCARD_TRANSACTION_DETAIL_URL = "https://services.jsatech.com/statement.php";
$WILDCARD_LOGIN_CHECK_URL = "https://services.jsatech.com/login-check.php";
$SKEY_LENGTH = 32;
$HEADER_DATA = getHTTPRequestHeaders();

/**
 *       DESCRIPTION:    Creates a valid Wildcard Office authenticated session and returns an authenticated session key (SKEY) value, or FALSE if the session was not created.
 **/
function getWildcardSession($studentID, $password) {
    global $WILDCARD_AUTH_URL, $SKEY_LENGTH, $WILDCARD_LOGIN_CHECK_URL, $HEADER_DATA;

    // We start by fetching the Wildcard Office website login form.  It contains a hidden form field value "skey", the session key, that we must obtain.
    // Create a new instance of Snoopy, the page fetching / parsing engine.
    $loginPage = new Snoopy;
     
    // We can set a timeout, in case this thing doesn't work, but the Wildcard server is unreliable, so we'll leave it be for now.
    // TODO: Determine if we need a timeout set here.
    //      $loginPage -> read_timeout = 5;
     
    // We will need to fetch pages using HTTPS, so we must point Snoopy to our instance of cURL.
    // This information is stored in our environment configuration.
    $loginPage -> curl_path = getCurlPath();
     
    // Set up appropriate HTTP header data.
    $loginPage -> rawheaders = $HEADER_DATA;

    // The JSA services site is running some strange altered version of Apache.  If you provide a port in the host header, the server bails on the request.
    // This is Snoopy's default action, so we need to alter that.
    $loginPage -> port = FALSE;
     
    // The we're going to be sending URL parameters over a GET request using HTTPS, so we must set this custom-created Snoopy behavior.
    $loginPage -> escape_shell = FALSE;

    if ($loginPage -> fetch($WILDCARD_AUTH_URL . "?cid=29")) {
        $searchQueryStart = "<input type='hidden' name='skey' value='";
        $searchQueryEnd = "'";
        $startIndex = strpos($loginPage -> results, $searchQueryStart) + strlen($searchQueryStart);
        if ($startIndex === FALSE) {
            // The session key could not be found, return FALSE
            return FALSE;
        } else {
            $endIndex = strpos($loginPage -> results, $searchQueryEnd, $startIndex);
            $skey = trim(substr($loginPage -> results, $startIndex, $endIndex - $startIndex));
        }
    } else {
        // Downloading the remote page failed, return FALSE.
        return FALSE;
    }
     
    // Check the validity of the session key.
    // The skey value will always be 32 characters long.
    if (strlen($skey) != $SKEY_LENGTH) {
        return FALSE;
    }

    // Continue by posting the login information to the authentication URL.
    $postData["save"] = "1";
    $postData["skey"] = $skey;
    $postData["cid"] = "29";
    $postData["wason"] = "";
    $postData["loginphrase"] = $studentID;
    $postData["password"] = $password;
     
    if ($loginPage -> submit($WILDCARD_AUTH_URL, $postData)) {

        // Check if the login information was incorrect
        if (strpos($loginPage -> results, "Invalid login or password") !== FALSE) {
            // Login information was incorrect, return FALSE.
            return FALSE;
        }
        // Get the next skey for authentication.
        $searchQueryStart = "?skey=";
        $searchQueryEnd = "&";
        $startIndex = strpos($loginPage -> results, $searchQueryStart) + strlen($searchQueryStart);
        if ($startIndex === FALSE) {
            // The session key could not be found, return FALSE
            return FALSE;
        } else {
            $endIndex = strpos($loginPage -> results, $searchQueryEnd, $startIndex);
            $skey = trim(substr($loginPage -> results, $startIndex, $endIndex - $startIndex));
        }
    } else {
        // Downloading the remote page failed, return FALSE.
        return FALSE;
    }
     
    // Check the validity of the session key.
    // The skey value will always be 32 characters long.
    if (strlen($skey) != $SKEY_LENGTH) {
        return FALSE;
    }

    // One final request is necessary to complete the session transaction.
    if ($loginPage -> fetch($WILDCARD_AUTH_URL . "?skey=" . $skey . "&cid=29&fullscreen=1&wason=")) {
        // wait for 1 second
        usleep(2000000);
        // Get the final skey for authentication.
        $searchQueryStart = "login-check.php?skey=";
        $searchQueryEnd = '";';
        $startIndex = strpos($loginPage -> results, $searchQueryStart) + strlen($searchQueryStart);
        if ($startIndex === FALSE) {
            // The session key could not be found, return FALSE
            return FALSE;
        } else {
            $endIndex = strpos($loginPage -> results, $searchQueryEnd, $startIndex);
            $skey = trim(substr($loginPage -> results, $startIndex, $endIndex - $startIndex));
             
            if (strlen($skey) != $SKEY_LENGTH) {
                return FALSE;
            }
            // Final authentication of skey
            if ($loginPage -> fetch($WILDCARD_LOGIN_CHECK_URL . "?skey=" . $skey)) {
                return $skey;
            } else {
                return FALSE;
            }
        }
    } else {
        // Downloading the remote page failed, return FALSE.
        return FALSE;
    }
}

/**
 *       DESCRIPTION:    Returns an array of all account names, IDs, and balances for which information is availible.
 **/
function getAccountData($sessionData) {
    global $WILDCARD_ACCOUNT_URL, $HEADER_DATA;
     
    // We must now fetch the wildcard account page.
    // Set up Snoopy with the required parameters.
    $accountPage = new Snoopy;
    $accountPage -> curl_path = getCurlPath();
    $accountPage -> rawheaders = $HEADER_DATA;
    $accountPage -> port = FALSE;
    $accountPage -> escape_shell = FALSE;
     
    $accountData = array();
    $accountName = "";
    $accountBalance = 0;

    if ($accountPage -> fetch($WILDCARD_ACCOUNT_URL . "?skey=" . $sessionData . "&cid=29")) {
        // Locate and parse the current balance from the Wildcard Restricted Debit Account
        $searchFormTitleStart = "<font class='formtitle'>";
        $searchFormTitleEnd = "</font>";
        $searchQueryStart = "<b>Current Balance: ";
        $searchQueryEnd = "</b>";
        $searchAccountStart = "&cid=29&acctto=";
        $searchAccountEnd = "' >View More</a>";
        $haystack = $accountPage -> results;
         
        $startIndex = strpos($haystack, $searchFormTitleStart) + strlen($searchFormTitleStart);
         
        while ($startIndex !== FALSE) {
            // Get account name
            $endIndex = strpos($haystack, $searchFormTitleEnd, $startIndex);
            $accountName = trim(substr($haystack, $startIndex, $endIndex - $startIndex));
             
            // Get account balance
            $startIndex = strpos($haystack, $searchQueryStart) + strlen($searchQueryStart);
            $endIndex = strpos($haystack, $searchQueryEnd, $startIndex);
            $accountBalance = trim(substr($haystack, $startIndex, $endIndex - $startIndex));
             
            // Get account ID
            $startIndex = strpos($haystack, $searchAccountStart) + strlen($searchAccountStart);
            $endIndex = strpos($haystack, $searchAccountEnd, $startIndex);
            $accountID = trim(substr($haystack, $startIndex, $endIndex - $startIndex));
             
            $haystack = substr($haystack, $endIndex);
            $startIndex = strpos($haystack, $searchFormTitleStart);
            if ($startIndex !== FALSE) {
                $startIndex += strlen($searchFormTitleStart);
            }
            $accountData[] = array(
                                                                   "id"                 =>      $accountID,
                                                                   "name"               =>      $accountName,
                                                                   "balance"    =>      $accountBalance
            );
        }
    } else {
        // Downloading the remote page failed, return FALSE.
        return FALSE;
    }
    return $accountData;
}

/**
 *       DESCRIPTION:    Returns an array of transaction data for a given account.
 **/
function getTransactionDetails($sessionData, $accountID) {
    global $WILDCARD_TRANSACTION_DETAIL_URL, $HEADER_DATA;
     
    // We must now fetch the wildcard transaction page.
    // Set up Snoopy with the required parameters.
    $transactionPage = new Snoopy;
    $transactionPage -> curl_path = getCurlPath();
    $transactionPage -> rawheaders = $HEADER_DATA;
    $transactionPage -> port = FALSE;
    $transactionPage -> escape_shell = FALSE;
     
    // We'll be posting a request to the form on this page to obtain data for the last six months, the maximum allowable account detail period.
    $postData["save"] = "1";
    $postData["skey"] = $sessionData;
    $postData["cid"] = "29";
    $postData["acctto"] = $accountID;
    $postData["month"] = "13";


    if ($transactionPage -> submittext($WILDCARD_TRANSACTION_DETAIL_URL . "?skey=" . $sessionData . "&cid=29", $postData)) {
        $searchQueryStart = "Balance";
        $searchQueryEnd = "Ending Balance:";
        // The keywork we're looking for appears twice on this page.  We want the data block to begin after the second occurence.
        $first = strpos($transactionPage -> results, $searchQueryStart) + strlen($searchQueryStart);
        $startIndex = strpos($transactionPage -> results, $searchQueryStart, $first) + strlen($searchQueryStart);
        if ($startIndex === FALSE) {
            // The data block of transactions could not be found, return FALSE
            return FALSE;
        } else {
            $endIndex = strpos($transactionPage -> results, $searchQueryEnd, $startIndex);
            $dataBlock = trim(substr($transactionPage -> results, $startIndex, $endIndex - $startIndex));
        }
    } else {
        // Downloading the remote page failed, return FALSE.
        return FALSE;
    }

    // Delimit the string of transaction data by the line seperator, and place it in an array.
    $transactionData = array();
    $transactionData = explode("\r", $dataBlock);

    // Verify the resulting data array
    // We know there are 4 pieces of information for each transaction record, so the size of the array should be a multiple of 4.
    if ((count($tranactionData) % 4) != 0) {
        return FALSE;
    } else {
        // Populate a 2D formatted array with the data
        $formattedData = array();
        $curPos = 0;
        while ($curPos < count($transactionData)) {
            $thisResult = array(
                                                                "date"          => $transactionData[$curPos],
                                                                "location"      => $transactionData[$curPos + 1],
                                                                "amount"        => $transactionData[$curPos + 2],
                                                                "balance"       => $transactionData[$curPos + 3]
            );
            $formattedData[] = $thisResult;
            $curPos = $curPos + 4;
        }
    }
    return $formattedData;
}
?>
